﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace Wicresoft.XslDebugger
{
    static class IExplorerSetting
    {
        #region Fields

        private const String mC_IExplorerSettingRegPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\"
                                                        + @"Windows\CurrentVersion\Internet Settings";
        private const String mC_UserAgent = "User Agent";
        private const String mC_ProxyServer = "ProxyServer";
        private const String mC_ProxyServer_Http = "ProxyServer|http=";

        #endregion

        #region Constructor

        static IExplorerSetting()
        { }

        #endregion

        #region Methods

        private static String GetSettingString(String settingName)
        {
            Debug.Assert((!String.IsNullOrEmpty(settingName)), "Can not pass in a null or empty 'settingName'");

            String retVal = String.Empty;
            Int32 splitIdx = 0;

            splitIdx = settingName.IndexOf('|');
            if (splitIdx >= 0)
            {
                if (settingName.StartsWith(mC_ProxyServer, StringComparison.Ordinal))
                {
                    String[] proxySrvArr = null;
                    String proxyTag = settingName.Substring(splitIdx + 1);

                    retVal = Microsoft.Win32.Registry.GetValue(mC_IExplorerSettingRegPath,
                                                               mC_ProxyServer, String.Empty).ToString();
                    if (retVal.IndexOf(';') >= 0)
                    {
                        proxySrvArr = retVal.Split(';');
                        foreach (String eachProxy in proxySrvArr)
                        {
                            if (eachProxy.StartsWith(proxyTag, StringComparison.OrdinalIgnoreCase))
                            {
                                retVal = eachProxy.Substring(proxyTag.Length);
                                break;
                            }
                        }
                    }
                }
            }
            else
            {
                retVal = Microsoft.Win32.Registry.GetValue(mC_IExplorerSettingRegPath,
                                                           settingName, String.Empty).ToString();
            }

            return retVal;
        }

        #endregion

        #region Property

        public static String UserAgent
        {
            get { return GetSettingString(mC_UserAgent); }
        }

        public static String ProxyServer
        {
            get { return GetSettingString(mC_ProxyServer); }
        }

        public static String ProxyServer_Http
        {
            get { return GetSettingString(mC_ProxyServer_Http); }
        }

        #endregion
    }

    static class HttpWebUtil
    {
        #region Fields

        private static Regex mS_InputElemRegex;
        private static Regex mS_NameAttrRegex;
        private static Regex mS_ValueAttrRegex;

        #endregion

        #region Constructor

        static HttpWebUtil()
        {
            RegexOptions regexOpt = RegexOptions.Compiled;
            String inputElemPat = "<input[^>]*>";
            String nameAttrPat = "name\\s*=\\s*\"([^\"]*)\"";
            String valueAttrPat = "value\\s*=\\s*\"([^\"]*)\"";

            mS_InputElemRegex = new Regex(inputElemPat, regexOpt);
            mS_NameAttrRegex = new Regex(nameAttrPat, regexOpt);
            mS_ValueAttrRegex = new Regex(valueAttrPat, regexOpt);
        }

        #endregion

        #region Methods

        public static WebProxy GetIExplorerProxy()
        {
            return (new WebProxy(IExplorerSetting.ProxyServer_Http));
        }

        public static Boolean GetPageHtmlString(String url, Encoding encoding, Boolean withProxy, 
            out String htmlStr, out String errInfo,
            bool Convent, bool CheckRequestEncoding)
        {
            Debug.Assert((!String.IsNullOrEmpty(url)), "Can not pass in a null or empty 'url'.");
            Debug.Assert((null != encoding), "Can not pass in a null 'encode'.");

            Boolean state = true;
            HttpWebRequest webRequest = null;
            Stream responseStream = null;

            errInfo = String.Empty;
            htmlStr = String.Empty;

            try
            {
                webRequest = WebRequest.Create(url) as HttpWebRequest;
                if (null != webRequest)
                {
                    webRequest.Method = "GET";
                    if (withProxy)
                        webRequest.Proxy = GetIExplorerProxy();

                    responseStream = webRequest.GetResponse().GetResponseStream();
                    string ResultStringContent;
                    string PreStringContent;
                    using (StreamReader sr = new StreamReader(responseStream, encoding))
                    {
                        PreStringContent = sr.ReadToEnd();
                        if (CheckRequestEncoding)
                        {
                            char[] charContent = PreStringContent.ToCharArray();
                            byte[] byteContent = encoding.GetBytes(charContent);
                            ResultStringContent = TryToGetEncoding(PreStringContent, encoding).GetString(byteContent);
                        }
                        else
                        {
                            ResultStringContent = PreStringContent;
                        }

                    }
                    if (Convent)
                    {
                        //converter the html to xhtml
                        htmlStr = HtmlConventer.ConventToXhtml(ResultStringContent);
                    }
                    else
                    {
                        htmlStr = ResultStringContent;
                    }
                }
                else
                {
                    errInfo = String.Format("Invalid url: {0}", url);
                    state = false;
                }
            }
            catch (Exception ex)
            {
                errInfo = ex.Message;
                state = false;
            }
            finally
            {
                if (null != responseStream)
                    responseStream.Close();
            }

            return state;
        }

        public static Boolean GetPageHtmlStringPost(String url, NameValueCollection paramCol, Encoding encoding, Boolean withProxy, 
            out String htmlStr, out String errInfo,
            bool Convent, bool CheckRequestEncoding)
        {
            Debug.Assert((!String.IsNullOrEmpty(url)), "Can not pass in a null or empty 'url'.");
            Debug.Assert((null != encoding), "Can not pass in a null 'encode'.");

            Boolean state = true;
            HttpWebRequest webRequest = null;
            Stream responseStream = null;

            errInfo = String.Empty;
            htmlStr = String.Empty;

            try
            {
                webRequest = WebRequest.Create(url) as HttpWebRequest;
                if (null != webRequest)
                {
                    webRequest.Method = "POST";
                    webRequest.ContentType = "application/x-www-form-urlencoded";
                    if (withProxy)
                        webRequest.Proxy = GetIExplorerProxy();
                    if (paramCol.Count > 0)
                    {
                        Byte[] paramByteArr = encoding.GetBytes(BuildParamString(paramCol, true, encoding));

                        webRequest.ContentLength = paramByteArr.Length;
                        using (Stream s = webRequest.GetRequestStream())
                        {
                            s.Write(paramByteArr, 0, paramByteArr.Length);
                            s.Flush();
                        }
                    }

                    responseStream = webRequest.GetResponse().GetResponseStream();

                    string ResultStringContent;
                    string PreStringContent;

                    using (StreamReader sr = new StreamReader(responseStream, encoding))
                    {
                        PreStringContent = sr.ReadToEnd();
                        if (CheckRequestEncoding)
                        {
                            char[] charContent = PreStringContent.ToCharArray();
                            byte[] byteContent = encoding.GetBytes(charContent);
                            ResultStringContent = TryToGetEncoding(PreStringContent, encoding).GetString(byteContent);
                        }
                        else
                        {
                            ResultStringContent = PreStringContent;
                        }
                    }
                    if (Convent)
                    {
                        //converter the html to xhtml
                        htmlStr = HtmlConventer.ConventToXhtml(ResultStringContent);
                    }
                    else
                    {
                        htmlStr = ResultStringContent;
                    }
                }
                else
                {
                    errInfo = String.Format("Invalid url: {0}", url);
                    state = false;
                }
            }
            catch (Exception ex)
            {
                errInfo = ex.Message;
                state = false;
            }
            finally
            {
                if (null != responseStream)
                    responseStream.Close();
            }

            return state;
        }

        public static Boolean GetPageHtmlStringGet(String url, NameValueCollection paramCol, Encoding encoding, Boolean withProxy, 
            out String htmlStr, out String errInfo,
            bool Convent, bool CheckRequestEncoding)
        {
            Debug.Assert((!String.IsNullOrEmpty(url)), "Can not pass in a null or empty 'url'.");
            Debug.Assert((null != encoding), "Can not pass in a null 'encode'.");

            String paramStr = String.Empty;
            String urlWithParam = String.Empty;

            paramStr = BuildParamString(paramCol, true, encoding);
            urlWithParam = (String.IsNullOrEmpty(paramStr) ? url : String.Concat(url, "?", BuildParamString(paramCol, true, encoding)));

            return GetPageHtmlString(urlWithParam, encoding, withProxy, out htmlStr, out errInfo,Convent,CheckRequestEncoding);
        }

        public static String BuildParamString(NameValueCollection paramCol, Boolean isEncodeVal, Encoding encoding)
        {
            Debug.Assert((null != paramCol), "Can not pass in a null 'paramCol'.");
            if (isEncodeVal)
                Debug.Assert((null != encoding), "Can not pass in a null 'encode'.");

            StringBuilder paramsStrB = new StringBuilder();
            Char[] chArr = null;
            Byte[] byteArr = (isEncodeVal ? new Byte[8] : null);

            foreach (String eachName in paramCol.AllKeys)
            {
                paramsStrB.Append(eachName).Append('=');
                if (isEncodeVal)
                {
                    chArr = paramCol[eachName].ToCharArray();
                    for (Int32 chIdx = 0; chIdx < chArr.Length; ++chIdx)
                    {
                        Char curCh = chArr[chIdx];

                        if (!(Char.IsLetterOrDigit(curCh) && Convert.ToUInt16(curCh) < 0xff))
                        {
                            Int32 byteCount = encoding.GetBytes(chArr, chIdx, 1, byteArr, 0);
                            for (Int32 byteIdx = 0; byteIdx < byteCount; ++byteIdx)
                                paramsStrB.Append('%').Append(byteArr[byteIdx].ToString("X2"));
                        }
                        else
                        {
                            paramsStrB.Append(chArr[chIdx]);
                        }
                    }
                }
                else
                {
                    paramsStrB.Append(paramCol[eachName]);
                }
                paramsStrB.Append('&');
            }
            if (paramsStrB.Length > 0)
                paramsStrB.Remove((paramsStrB.Length - 1), 1);

            return paramsStrB.ToString();
        }

        public static Boolean IsPostBackPage(String htmlStr)
        {
            Debug.Assert(!String.IsNullOrEmpty(htmlStr), "Can not pass in a null or empty 'htmlStr'.");

            Boolean isPostBackPage = false;
            String formElemStr = "<form";
            Char endElemCh = '>';
            Int32 formIdx = 0;
            Int32 formLen = 0;
            String postKeyWord = "\"post\"";
            StringComparison strCpr = StringComparison.OrdinalIgnoreCase;

            formIdx = htmlStr.IndexOf(formElemStr, strCpr);
            if (formIdx >= 0)
            {
                formLen = htmlStr.IndexOf(endElemCh, formIdx) - formIdx;
                if (formLen > 0)
                    isPostBackPage = (htmlStr.IndexOf(postKeyWord, formIdx, formLen, strCpr) >= 0);
            }

            return isPostBackPage;
        }

        public static NameValueCollection GetPostParams(String htmlStr)
        {
            Debug.Assert(!String.IsNullOrEmpty(htmlStr), "Can not pass in a null or empty 'htmlStr'.");

            NameValueCollection paramCol = new NameValueCollection();

            foreach (Match eachInputMatch in mS_InputElemRegex.Matches(htmlStr))
            {
                String inputElemStr = eachInputMatch.Groups[0].Value;
                Match nameMatch = mS_NameAttrRegex.Match(inputElemStr);
                Match valueMatch = mS_ValueAttrRegex.Match(inputElemStr);

                if (nameMatch.Success)
                {
                    if (valueMatch.Success)
                        paramCol.Add(nameMatch.Groups[1].Value, valueMatch.Groups[1].Value);
                    else
                        paramCol.Add(nameMatch.Groups[1].Value, String.Empty);
                }
            }

            return paramCol;
        }


        private static Encoding TryToGetEncoding(string content, Encoding tryEncoding)
        {
            /*try to parse html*/
            MatchCollection mc = Regex.Matches(content, "<meta [^>]*>", RegexOptions.IgnoreCase);
            foreach (Match m in mc)
            {
                if (m != null && m.Value != string.Empty)
                {
                    Match mm = Regex.Match(m.Value, @"charset[ \t]*=[ \t]*[\w-_]+");
                    if (mm != null && mm.Value != string.Empty)
                    {
                        string s = mm.Value;
                        int start = s.IndexOf('=') + 1; 
                        Encoding retEncoding = null;
                        try
                        {
                            retEncoding = Encoding.GetEncoding(s.Substring(start, s.Length - start).Trim());
                        }
                        catch
                        {
                        }
                        if (retEncoding != null)
                            return retEncoding;
                    }
                }
            }

            /*try to parse xml*/
            Match xmlhead = Regex.Match(content, "<?xml [^\r\n]+?>");
            if (xmlhead != null && xmlhead.Value != string.Empty)
            {
                Match xmlencoding = Regex.Match(xmlhead.Value, string.Format(@"encoding[ \t]*=[ \t]*{0}[\w-_]+{0}", "\""));
                if (xmlencoding != null && xmlencoding.Value != string.Empty)
                {
                    string s = xmlencoding.Value;
                    int start = s.IndexOf('=') + 2;
                    Encoding retEncoding = null;
                    try
                    {
                        retEncoding = Encoding.GetEncoding(s.Substring(start, s.Length - start - 1).Trim());
                    }
                    catch
                    {
                    }
                    if (retEncoding != null)
                        return retEncoding;
                }
            }

            EncodingReChoose ercfrm = new EncodingReChoose();
            if (ercfrm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                return ercfrm.chooseEncoding;
            }
            else
            {
                return tryEncoding;
            }
        }

        #endregion
    }
}
